package com.fc.test.shiro.config;

import org.apache.shiro.authc.HostAuthenticationToken;
import org.apache.shiro.authc.RememberMeAuthenticationToken;

/**
 * describe: 令牌（手机+密码）
 *
 * @author xugang
 * @date 2018/9/19 17:36
 */
public class MobilePhonePasswordToken implements HostAuthenticationToken, RememberMeAuthenticationToken {

    /**
     * 手机号码
     */
    private String mobilePhone;

    /**
     * The password, in char[] format
     */
    private char[] password;

    /**
     * Whether or not 'rememberMe' should be enabled for the corresponding login attempt;
     * default is <code>false</code>
     */
    private boolean rememberMe = false;

    /**
     * The location from where the login attempt occurs, or <code>null</code> if not known or explicitly
     * omitted.
     */
    private String host;

    /**
     * JavaBeans compatible no-arg constructor.
     */
    public MobilePhonePasswordToken() {
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted
     * during an authentication attempt, with a <tt>null</tt> {@link #getHost() host} and a
     * <tt>rememberMe</tt> default of <tt>false</tt>.
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password character array submitted for authentication
     */
    public MobilePhonePasswordToken(final String mobilePhone, final char[] password) {
        this(mobilePhone, password, false, null);
    }


    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted
     * during an authentication attempt, with a <tt>null</tt> {@link #getHost() host} and
     * a <tt>rememberMe</tt> default of <tt>false</tt>
     * <p/>
     * <p>This is a convenience constructor and maintains the password internally via a character
     * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a String
     * in your code could have possible security implications as noted in the class JavaDoc.</p>
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     */
    public MobilePhonePasswordToken(final String mobilePhone, final String password) {
        this(mobilePhone, password != null ? password.toCharArray() : null, false, null);
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, the
     * inetAddress from where the attempt is occurring, and a default <tt>rememberMe</tt> value of <tt>false</tt>
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     * @param host        the host name or IP string from where the attempt is occurring
     * @since 0.2
     */
    public MobilePhonePasswordToken(final String mobilePhone, final char[] password, final String host) {
        this(mobilePhone, password, false, host);
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, the
     * inetAddress from where the attempt is occurring, and a default <tt>rememberMe</tt> value of <tt>false</tt>
     * <p/>
     * <p>This is a convenience constructor and maintains the password internally via a character
     * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a String
     * in your code could have possible security implications as noted in the class JavaDoc.</p>
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     * @param host        the host name or IP string from where the attempt is occurring
     * @since 1.0
     */
    public MobilePhonePasswordToken(final String mobilePhone, final String password, final String host) {
        this(mobilePhone, password != null ? password.toCharArray() : null, false, host);
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, as well as if the user
     * wishes their identity to be remembered across sessions.
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     * @param rememberMe  if the user wishes their identity to be remembered across sessions
     * @since 0.9
     */
    public MobilePhonePasswordToken(final String mobilePhone, final char[] password, final boolean rememberMe) {
        this(mobilePhone, password, rememberMe, null);
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, as well as if the user
     * wishes their identity to be remembered across sessions.
     * <p/>
     * <p>This is a convenience constructor and maintains the password internally via a character
     * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a String
     * in your code could have possible security implications as noted in the class JavaDoc.</p>
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     * @param rememberMe  if the user wishes their identity to be remembered across sessions
     * @since 0.9
     */
    public MobilePhonePasswordToken(final String mobilePhone, final String password, final boolean rememberMe) {
        this(mobilePhone, password != null ? password.toCharArray() : null, rememberMe, null);
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, if the user
     * wishes their identity to be remembered across sessions, and the inetAddress from where the attempt is occurring.
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password character array submitted for authentication
     * @param rememberMe  if the user wishes their identity to be remembered across sessions
     * @param host        the host name or IP string from where the attempt is occurring
     * @since 1.0
     */
    public MobilePhonePasswordToken(final String mobilePhone, final char[] password,
                                    final boolean rememberMe, final String host) {

        this.mobilePhone = mobilePhone;
        this.password = password;
        this.rememberMe = rememberMe;
        this.host = host;
    }

    /**
     * Constructs a new MobilePhonePasswordToken encapsulating the mobilePhone and password submitted, if the user
     * wishes their identity to be remembered across sessions, and the inetAddress from where the attempt is occurring.
     * <p/>
     * <p>This is a convenience constructor and maintains the password internally via a character
     * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a String
     * in your code could have possible security implications as noted in the class JavaDoc.</p>
     *
     * @param mobilePhone the mobilePhone submitted for authentication
     * @param password    the password string submitted for authentication
     * @param rememberMe  if the user wishes their identity to be remembered across sessions
     * @param host        the host name or IP string from where the attempt is occurring
     * @since 1.0
     */
    public MobilePhonePasswordToken(final String mobilePhone, final String password,
                                    final boolean rememberMe, final String host) {
        this(mobilePhone, password != null ? password.toCharArray() : null, rememberMe, host);
    }

    /**
     * Returns the mobilePhone submitted during an authentication attempt.
     *
     * @return the mobilePhone submitted during an authentication attempt.
     */
    public String getMobilePhone() {
        return mobilePhone;
    }

    /**
     * Sets the mobilePhone for submission during an authentication attempt.
     *
     * @param mobilePhone the mobilePhone to be used for submission during an authentication attempt.
     */
    public void setMobilePhone(String mobilePhone) {
        this.mobilePhone = mobilePhone;
    }

    /**
     * Returns the password submitted during an authentication attempt as a character array.
     *
     * @return the password submitted during an authentication attempt as a character array.
     */
    public char[] getPassword() {
        return password;
    }

    /**
     * Sets the password for submission during an authentication attempt.
     *
     * @param password the password to be used for submission during an authentication attempt.
     */
    public void setPassword(char[] password) {
        this.password = password;
    }

    /**
     * Simply returns {@link #getMobilePhone() getMobilePhone()}.
     *
     * @return the {@link #getMobilePhone() mobilePhone}.
     * @see org.apache.shiro.authc.AuthenticationToken#getPrincipal()
     */
    @Override
    public Object getPrincipal() {
        return getMobilePhone();
    }

    /**
     * Returns the {@link #getPassword() password} char array.
     *
     * @return the {@link #getPassword() password} char array.
     * @see org.apache.shiro.authc.AuthenticationToken#getCredentials()
     */
    @Override
    public Object getCredentials() {
        return getPassword();
    }

    /**
     * Returns the host name or IP string from where the authentication attempt occurs.  May be <tt>null</tt> if the
     * host name/IP is unknown or explicitly omitted.  It is up to the Authenticator implementation processing this
     * token if an authentication attempt without a host is valid or not.
     * <p/>
     * <p>(Shiro's default Authenticator allows <tt>null</tt> hosts to support localhost and proxy server environments).</p>
     *
     * @return the host from where the authentication attempt occurs, or <tt>null</tt> if it is unknown or
     * explicitly omitted.
     * @since 1.0
     */
    public String getHost() {
        return host;
    }

    /**
     * Sets the host name or IP string from where the authentication attempt occurs.  It is up to the Authenticator
     * implementation processing this token if an authentication attempt without a host is valid or not.
     * <p/>
     * <p>(Shiro's default Authenticator
     * allows <tt>null</tt> hosts to allow localhost and proxy server environments).</p>
     *
     * @param host the host name or IP string from where the attempt is occurring
     * @since 1.0
     */
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * Returns <tt>true</tt> if the submitting user wishes their identity (principal(s)) to be remembered
     * across sessions, <tt>false</tt> otherwise.  Unless overridden, this value is <tt>false</tt> by default.
     *
     * @return <tt>true</tt> if the submitting user wishes their identity (principal(s)) to be remembered
     * across sessions, <tt>false</tt> otherwise (<tt>false</tt> by default).
     * @since 0.9
     */
    public boolean isRememberMe() {
        return rememberMe;
    }

    /**
     * Sets if the submitting user wishes their identity (principal(s)) to be remembered across sessions.  Unless
     * overridden, the default value is <tt>false</tt>, indicating <em>not</em> to be remembered across sessions.
     *
     * @param rememberMe value indicating if the user wishes their identity (principal(s)) to be remembered across
     *                   sessions.
     * @since 0.9
     */
    public void setRememberMe(boolean rememberMe) {
        this.rememberMe = rememberMe;
    }

    /**
     * Clears out (nulls) the mobilePhone, password, rememberMe, and inetAddress.  The password bytes are explicitly set to
     * <tt>0x00</tt> before nulling to eliminate the possibility of memory access at a later time.
     */
    public void clear() {
        this.mobilePhone = null;
        this.host = null;
        this.rememberMe = false;

        if (this.password != null) {
            for (int i = 0; i < password.length; i++) {
                this.password[i] = 0x00;
            }
            this.password = null;
        }

    }

    /**
     * Returns the String representation.  It does not include the password in the resulting
     * string for security reasons to prevent accidentally printing out a password
     * that might be widely viewable).
     *
     * @return the String representation of the <tt>UsernamePasswordToken</tt>, omitting
     * the password.
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getName());
        sb.append(" - ");
        sb.append(mobilePhone);
        sb.append(", rememberMe=").append(rememberMe);
        if (host != null) {
            sb.append(" (").append(host).append(")");
        }
        return sb.toString();
    }
}
